// generated 2005/6/24 0:15:11 CST by xihels@163.com
//


#if defined __GNUC__ && __GNUC__ < 3
#error This program will crash if compiled with g++ 2.x
// see the dynamic_cast bug in the gtkmm FAQ
#endif //
#include "config.h"
#include <gtkmmconfig.h>
#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
#define GMM_GTKMM_22_24(a,b) b
#else //gtkmm 2.2
#define GMM_GTKMM_22_24(a,b) a
#endif //
#include <gdk/gdkkeysyms.h>
#include <gtkmm/accelgroup.h>
#include <gtkmm/buttonbox.h>
#include <gtkmm/label.h>
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include <gdkmm/pixbufloader.h>
#include <gtkmm/table.h>
#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
#include <gtkmm/expander.h>
#else //
#include <gtkmm/handlebox.h>
#endif //

#include "QQUser.h"

#include "pixmaps.h"
#include "config.h"
#include "LoginWindow.h"
#include "MainWindow.h"
#include <sstream>
#include <stdlib.h>

#include "ConfFile.h"
using namespace std;

#define ACCOUNT_CHANGE	1
#define PASSWD_CHANGE	2


using namespace std;
namespace 
{
    string md52str(const uint8_t* const key)
    {
	char buf[QQ_KEY_LENGTH * 2];
	int j = 0;
	for (int i = 0; i < QQ_KEY_LENGTH; i++) {
	    buf[j++] = (key[i] >> 4) + '*';
	    buf[j++] = (key[i] & 0x0f) + '*' ;
	}
	buf[j] = '\0';
	return string(buf);
    }

    void str2md5(const string& str, uint8_t* const key)
    {
	if (str.size() < 32)
	    return;
	int j = 0;
	char c;
	for (int i = 0; i < QQ_KEY_LENGTH; i++){
	    c = (str[j++] - '*') << 4;
	    c |= str[j++] - '*';
	    key[i] = c;
	}
    }

    bool writer_to_file(ConfFile& cf, const QQUser& user)
    {
	cf.write_int("QQ",  user.getUid());

	cf.write_string("IP", user.getServer());
	cf.write_int("PORT", user.getPort());
	cf.write_int("LOGINMODE", user.getLoginMode());
	cf.write_int("TCP", user.isTcp());
	cf.write_int("FAKE_CAM", user.isFakeCam());

	if (user.isRememberPassword())
	    cf.write_string("KEY", md52str(user.getPwkey()));
	else
	    cf.write_string("KEY", "");

	cf.write_int("REMEMBER_PASSWORD", user.isRememberPassword());
	return true;
    }

    bool read_from_file(ConfFile& cf, QQUser& user)
    {
	int itmp;
	string stmp;
	string name;

	cf.read_int(name, itmp);
	if (name != "QQ")
	    return false;
	user.setUid(itmp);



	cf.read_string(name, stmp);
	if (name != "IP")
	    return false;
	user.setServer(stmp);

	cf.read_int(name, itmp);
	if (name != "PORT")
	    return false;
	user.setPort((uint16_t)itmp);

	cf.read_int(name, itmp);
	if (name != "LOGINMODE")
	    return false;
	user.setLoginMode((uint8_t)itmp);

	cf.read_int(name, itmp);
	if (name != "TCP")
	    return false;
	user.setTcp((bool)itmp);

	cf.read_int(name, itmp);
	if (name != "FAKE_CAM")
	    return false;
	user.setFakeCam((bool)itmp);

	cf.read_string(name, stmp);
	if (name != "KEY")
	    return false;
	if (stmp.size() >= 32) {
	    uint8_t key[64];
	    str2md5(stmp, key);
	    user.setPwkey(key);
	}
	cf.read_int(name, itmp);
	if (name != "REMEMBER_PASSWORD")
	    return false;
	user.setRememberPassword((stmp.size() >= 32) && ((bool)itmp));
	return true;
    }

}

///////////////////////////////////////////////////////////////////////////////

LoginWindow::LoginWindow(MainWindow& _parent) :
    parent(_parent),
    passwdChange(false)
{  
    Gtk::Window *loginWindow = this;
    gmm_data = new GlademmData(get_accel_group());


    Gtk::Image *imageLogo = getImage("cyclone.png");

    Gtk::Label *labelAccount = 
	Gtk::manage(new class Gtk::Label("<b>账号(_A)</b>", true));
    Gtk::Label *labelPasswd = 
	Gtk::manage(new class Gtk::Label("<b>密码(_P)</b>", true));


    comboAccount = Gtk::manage(new class Gtk::ComboBoxEntryText());
    entryPasswd = Gtk::manage(new class Gtk::Entry());

    checkHide =
	Gtk::manage(new class Gtk::CheckButton("隐身登录(_H)", true));

    checkRememberPassword = 
	Gtk::manage(new class Gtk::CheckButton("保存密码(_S)", true));

    checkTcp = 
	Gtk::manage(new class Gtk::CheckButton("_TCP登录", true));

    Gtk::VBox *vboxOptions = Gtk::manage(new class Gtk::VBox(false, 0));
    Gtk::Label *labelOptions = Gtk::manage(new class Gtk::Label("详细选项(_D)", true));

#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
    Gtk::Expander *expanderOptions = Gtk::manage(new class Gtk::Expander());
#else //
    Gtk::HandleBox *expanderOptions = Gtk::manage(new class Gtk::HandleBox());
#endif //

    Gtk::Table *tableLogin = Gtk::manage(new class Gtk::Table(2, 2, false));
    Gtk::Button *buttonCancel = Gtk::manage(new class Gtk::Button("取消(_C)", true));

    buttonConnect = Gtk::manage(new class Gtk::Button("连接(_O)", true));

    Gtk::HBox *hboxAction = Gtk::manage(new class Gtk::HBox(false, 0));
    Gtk::VBox *vboxMain = Gtk::manage(new class Gtk::VBox(false, 0));

    imageLogo->set_alignment(0.5,0.5);
    imageLogo->set_padding(20,20);

    labelAccount->set_alignment(0,0.5);
    labelAccount->set_use_markup(true);

    labelPasswd->set_alignment(0,0.5);
    labelPasswd->set_use_markup(true);
    labelPasswd->set_mnemonic_widget(*entryPasswd);

    comboAccount->get_entry()->set_max_length(10);
    entryPasswd->set_visibility(false);
    entryPasswd->set_max_length(16);
    entryPasswd->set_text("");

    vboxOptions->pack_start(*checkHide, Gtk::PACK_SHRINK, 0);
    vboxOptions->pack_start(*checkRememberPassword, Gtk::PACK_SHRINK, 0);
    vboxOptions->pack_start(*checkTcp, Gtk::PACK_SHRINK, 0);

    labelOptions->set_alignment(0,0.5);

#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
    expanderOptions->set_expanded(false);
    expanderOptions->set_spacing(0);
#endif //
    expanderOptions->add(*vboxOptions);
#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
    expanderOptions->set_label_widget(*labelOptions);
#endif //

    tableLogin->set_border_width(10);
    tableLogin->set_row_spacings(5);
    tableLogin->set_col_spacings(5);
    tableLogin->attach(*labelAccount, 0, 1, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::AttachOptions(), 0, 0);
    tableLogin->attach(*labelPasswd, 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::AttachOptions(), 0, 0);
    tableLogin->attach(*comboAccount, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0);
    tableLogin->attach(*entryPasswd, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::AttachOptions(), 0, 0);
    tableLogin->attach(*expanderOptions, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);

    buttonCancel->set_relief(Gtk::RELIEF_HALF);

    buttonConnect->set_relief(Gtk::RELIEF_HALF);
    buttonConnect->set_flags(Gtk::CAN_DEFAULT);

    hboxAction->pack_start(*buttonCancel);
    hboxAction->pack_start(*buttonConnect);

    vboxMain->pack_start(*imageLogo, Gtk::PACK_EXPAND_PADDING, 0);
    vboxMain->pack_start(*tableLogin);
    vboxMain->pack_start(*hboxAction, Gtk::PACK_SHRINK, 0);

    loginWindow->set_title("登录");
    loginWindow->set_modal(false);
    loginWindow->property_window_position().set_value(Gtk::WIN_POS_CENTER_ALWAYS);
    loginWindow->set_resizable(false);
    loginWindow->property_destroy_with_parent().set_value(false);
    loginWindow->add(*vboxMain);

    buttonConnect->set_sensitive(false);
    // loginWindow->set_default(*buttonConnect);
    //
    loadUsersList();
    loginWindow->show_all();

    buttonCancel->add_accelerator("clicked",
	    gmm_data->getAccelGroup(), GDK_Escape, Gdk::ModifierType(0), Gtk::ACCEL_VISIBLE);
    buttonConnect->add_accelerator("clicked",
	    gmm_data->getAccelGroup(), GDK_Return, Gdk::ModifierType(0), Gtk::ACCEL_VISIBLE);
    buttonConnect->add_accelerator("clicked",
	    gmm_data->getAccelGroup(), GDK_KP_Enter, Gdk::ModifierType(0), Gtk::ACCEL_VISIBLE);

    comboAccount->signal_changed().connect(
	    sigc::mem_fun(*this, &::LoginWindow::on_account_changed), false);

    passwdChangeConnect = entryPasswd->signal_changed().connect(
	    sigc::mem_fun(*this, &::LoginWindow::on_passwd_changed), false);

    buttonConnect->signal_clicked().connect(
	    sigc::bind<LoginWindow*>(
		sigc::mem_fun(parent, &MainWindow::on_loginWindow_ok), this),
	    false);

    buttonCancel->signal_clicked().connect(
	    sigc::bind<LoginWindow*>(
		sigc::mem_fun(parent, &MainWindow::on_loginWindow_cancel), this),
	    false);

}

LoginWindow::~LoginWindow()
{
    delete gmm_data;
}

void LoginWindow::loadUsersList()
{

    char buf[512];
    snprintf(buf, 512, "%s/user.lst", GUnit::getHomePath());

    string name;
    int defuser;
    
    try {
	ConfFile is(buf, "r");
	is.read_int(name, defuser);
	if (name != "LAST_LOGIN") {
	    return;
	}
	QQUser user;
	while (read_from_file(is, user)){
	    users.push_back(user);
	    snprintf(buf, 512, "%d", user.getUid());
	    comboAccount->append_text(buf);
	}
    }
    catch (exception& e) {
	fprintf(stderr, "%s\n", e.what());
    }

    USERS::iterator iter = find_if(users.begin(), users.end(), compare(defuser));
    if (users.end() != iter) {
	char buf[32];
	snprintf(buf, 32, "%d", defuser);
	comboAccount->set_active_text(buf);
	User2Gui(iter);
    }
}

void LoginWindow::saveUsersList(int lastlogin)
{
    char buf[512];
    snprintf(buf, 512, "%s/user.lst",GUnit::getHomePath());

    try {
	ConfFile os(buf, "w");
	os.write_int("LAST_LOGIN", lastlogin);

	USERS::iterator first = users.begin();
	USERS::iterator last  = users.end();

	for (; first != last; ++first) {
	    writer_to_file(os, *first);
	}
    }
    catch (exception& e) {
	fprintf(stderr, "%s\n", e.what());
    }
}

bool LoginWindow::on_delete_event(GdkEventAny*)
{
    parent.on_loginWindow_cancel(this);
}

QQUser LoginWindow::getUser() 
{
    int uid = atoi(comboAccount->get_entry()->get_text().c_str());

    USERS::iterator iter = find_if(users.begin(), users.end(), compare(uid));
    if (users.end() == iter) {
	QQUser qquser;
	qquser.setUid(uid);
	users.push_back(qquser);
	iter = users.end();
	--iter;
    }

    Gui2User(iter);
    saveUsersList((*iter).getUid());    // 保存用户列表
    return *iter;
}

void LoginWindow::on_passwd_changed()
{
    // 用户键入了密码
    passwdChange = true;
    if (comboAccount->get_entry()->get_text_length() && 
	    entryPasswd->get_text_length()) {
	buttonConnect->set_sensitive(true);
    } else {
	buttonConnect->set_sensitive(false);
    }    
}

void LoginWindow::on_account_changed()
{
    if (!comboAccount->get_active_text().empty()){ // 用户改变了账号
	// 如果活动文本不空，
	// 说明用户是选择已有的项
	// 而不是键入文本

	// 加载保存的设置
	int id = atoi(comboAccount->get_active_text().c_str());

	USERS::iterator iter = find_if(users.begin(), users.end(), compare(id));
	if (iter != users.end()) {
	    User2Gui(iter);
	}
    } else if (!passwdChange) { // 用户输入了新的账号，但还没有更改过密码
	passwdChangeConnect.block(); 
	entryPasswd->set_text("");     // 清空本来的密码
	passwdChangeConnect.unblock();
	buttonConnect->set_sensitive(false);
    } else if (comboAccount->get_entry()->get_text_length() && 
	    entryPasswd->get_text_length()) {
	buttonConnect->set_sensitive(true);
    } else {
	buttonConnect->set_sensitive(false);
    }    
}

void LoginWindow::User2Gui(USERS::iterator iter)
{
    checkHide->set_active(
	    (*iter).getLoginMode() == QQ_LOGIN_MODE_HIDDEN);
    checkRememberPassword->set_active(
	    (*iter).isRememberPassword());
    checkTcp->set_active(
	    (*iter).isTcp());

    passwdChangeConnect.block();
    if ((*iter).isRememberPassword()) {
	entryPasswd->set_text("**********");
	buttonConnect->set_sensitive(true);
    } else {
	entryPasswd->set_text("");
	buttonConnect->set_sensitive(true);
    }
    passwdChangeConnect.unblock();
    passwdChange = false;

}
void LoginWindow::Gui2User(USERS::iterator iter)
{
    (*iter).setTcp(checkTcp->get_active());
    (*iter).setLoginMode(checkHide->get_active() ?
			 QQ_LOGIN_MODE_HIDDEN: QQ_LOGIN_MODE_NORMAL);
    (*iter).setRememberPassword(checkRememberPassword->get_active());

    if (passwdChange) {	// 用户已经更改过密码
	(*iter).setPassed(entryPasswd->get_text());
    }
}
